home *** CD-ROM | disk | FTP | other *** search
/ Technotools / Technotools (Chestnut CD-ROM)(1993).ISO / os2tools / os2life / life.c < prev    next >
Encoding:
C/C++ Source or Header  |  1988-02-29  |  36.5 KB  |  1,182 lines

  1. /***
  2.  *  Title:
  3.  *
  4.  *    LIFE - An OS/2 Game of Life
  5.  *
  6.  *  Created by Microsoft Corp. 1987
  7.  *
  8.  *  Description:
  9.  *
  10.  *    This is a OS/2 implementation of the game of Life.  It is designed
  11.  *    to be bound to allow it to operate in both the protect mode of OS/2
  12.  *    and in earlier MS-DOS versions.  The program uses a mouse if one
  13.  *    is installed, but one is not needed.
  14.  *
  15.  *    Keyboard commands:   B(lank) - redraw screen
  16.  *                 G(o)    - step through generations until a key or
  17.  *                       button is hit
  18.  *                 H(alt)  - freeze at current generation
  19.  *                 Q(uit)  - exit program
  20.  *                 R(ead)  - read a game board from the disk
  21.  *                 S(step) - advance a single generation
  22.  *                 W(rite) - write the current board to disk
  23.  *                 D(own-speed) - slow down GOs
  24.  *                 U(p-speed) - speed up GOs
  25.  *
  26.  */
  27.  
  28. #define INCL_DOS            /* includes all OS/2 calls in      */
  29. #define INCL_SUB            /* bsedos.h and bsesub.h      */
  30.  
  31. #include <os2def.h>            /* definitions for OS/2 calls and */
  32. #include <bse.h>            /*     associated data structures */
  33.  
  34. /* defines */
  35. #define SCR_WID 80            /* screen buffer row width in bytes */
  36. #define SIGNATURE 0x5342        /* 1st word of all files from this */
  37.  
  38. /* these defines are all coordinates to put various messsages */
  39. #define COM_ROW 23            /* alpha row for command line */
  40. #define COM_COL 0            /* column for same */
  41. #define PROMPT_ROW 24            /* alpha row for prompts */
  42. #define PROMPT_COL 0            /* column for same */
  43. #define GEN_COL 13            /* column to put generation number on*/
  44. #define FILE_COL 37            /* column to put filespec */
  45.  
  46. /* misc. defines */
  47. #define FONT_ROW 8            /* pixel rows in the font */
  48. #define BIT 8                /* number of bits in a byte */
  49. #define BYTEINCOLS 8            /* no. of interal columns in a byte */
  50.  
  51. /****    all functions in this file ******/
  52. int main();         /* setup and master loop for life game*/
  53. void blank();         /* blank internal and screen grid*/
  54. void go();         /* steps through generations until a key is pressed*/
  55. void halt();         /* does nothing */
  56. void quit();         /* exit after prompting for certainty*/
  57. void diskread();     /* read new grid from disk*/
  58. int step();         /* advance one generation on screen and internally*/
  59. void diskwrite();     /* write internal grid to disk*/
  60. void up();         /* speeds up GO if possible */
  61. void down();         /* slows down GO */
  62. void beep();         /* beep the speaker for bad commands*/
  63. void showingrid();     /* display internal grid on screen*/
  64. int getfilespec();     /* get a file name from the user*/
  65. void putgen();         /* puts the current generation counter on prompt line*/
  66. void fill();         /*  fill in a grid cell on the screen and internally*/
  67. void remove();         /* clear a grid cell on the screen and internal grids*/
  68. int kbdreadeventque();     /* simulates MouReadEventQue with the keyboard*/
  69. void xorptr();         /* xors the mouse ptr on the screen*/
  70. void gputs();         /* put a character string on the graphics screen*/
  71. void gputchar();     /* put a character on the graphics screen*/
  72. void anerror();      /* error handler*/
  73. char gethit();         /* waits for a key hit or mouse button hit*/
  74. void wait4release();     /* wait until mouse buttons are up before returning*/
  75. void far pascal exitlife();/* exit program, resetting original screen mode*/
  76.  
  77.  
  78. /* global data */
  79. /* global variables for the internal and screen grids */
  80. int InRow = 45;             /* rows & columns of cells w/ default*/
  81. int InCol = 80;             /*   in internal grid. Col must be /8*/
  82. int ScrRow = 45;            /* rows & column on screen grid */
  83. int ScrCol = 79;
  84. int SizeScrRow = 4;            /* pixel rows per screen grid row */
  85. int SizeScrCol = 8;            /* pixels cols per screen grid column*/
  86. PSZ InGrid;                /* pointer to internal grid, a simple
  87.                      * bit map of the space and cells */
  88. PSZ InGrid2;                /* pointer for secondary map used
  89.                      *   to calculate next generation */
  90.  
  91. /* screen related global data */
  92. static VIOMODEINFO Highres = {12, 3, 1, 80, 25, 640, 200}; /* 640x200 b/w*/
  93. VIOMODEINFO Savemode = {12};        /* place to save old screen mode */
  94. unsigned ScrSeg;            /* screen buffer segment address */
  95. char Cell[]={32,7};            /* blank for clear screen */
  96. unsigned OddPage=0x2000;        /* offset on Cga of odd row bit plane*/
  97.                     /*   should be 0 on non-Cga modes */
  98. unsigned Cga=2;             /* if using a Cga, screen adresses on
  99.                      *   must be divided by 2 because of
  100.                      *   the odd and even row bit planes
  101.                      * if using non-Cga mode, should be 1*/
  102. /* mouse related global data */
  103. HMOU Mouse = 0;             /* handle if mouse is present, else */
  104. int MouBoundRow = COM_ROW*FONT_ROW+6;    /* last row mouse is allowed on */
  105. int MouBoundCol = 631;            /* last column mouse is allowed on */
  106.  
  107. /* misc. global data */
  108. char Filespec[79-FILE_COL];        /* file name holder (allows default)*/
  109. unsigned Generation;            /* current generation count */
  110. char Logo[] = "                                                             \
  111.     Microsoft LIFE";                    /* logo used to clear prompt line */
  112. int Slow=0;                /* number of slow-down loops for Go */
  113.  
  114. /* Data for commands.  Column of command on COM_ROW for printed name,
  115.  *    name for printing on command line, flag if not 0 then the command
  116.  *    can be executed within a Go command without stopping execution,
  117.  *    and the function that does the command. */
  118. struct Commands {
  119.     int Col;
  120.     NPSZ Name;
  121.     char GoAble;
  122.     void (*Fun)();
  123. } ComLine[] = { 0, "Command:", 0, beep,
  124.         10, "Blank", 0, blank,
  125.         16, "Go", 0, go,
  126.         19, "Halt", 0, halt,
  127.         24, "Quit", 0, quit,
  128.         29, "Read", 0, diskread,
  129.         34, "Step", 0, step,
  130.         39, "Write", 0, diskwrite,
  131.         46, "Down-speed", 1, down,
  132.         57, "Up-speed", 1, 0,
  133.         67, 0, 0
  134.           };
  135.  
  136.  
  137. /***    main - setup and master loop for life game
  138.  *
  139.  *    First this sets the screen mode, gets the address of the screen buffer,
  140.  *    sets the ctrl-C handle to a routine to reset the screen mode on exit,
  141.  *    and allocates the data structures for the internal representation of
  142.  *    the life grid.    The main loop of the program continuously reads from
  143.  *    the keyboard and the mouse and translates the key or location of the
  144.  *    mouse with the ComLine structure into a command to execute.
  145.  */
  146. main () {
  147.     KBDKEYINFO kbd;                /* return for KBD call */
  148.     VIOPHYSBUF get_phys;            /* return for GetPhysBufData */
  149.     USHORT throwaway;                /* for returns I don't use */
  150.     int i;                    /* just a counter */
  151.     unsigned far *ptr;                /*used to clear internal grid*/
  152.  
  153.     /* data for the mouse (or keyboard emulator) */
  154.     int ptrrow = 86;        /* current pointer position */
  155.     int ptrcol = 316;
  156.     USHORT status = 0x100;    /* for MouSetDevStatus */
  157.     USHORT type=0;        /* for no waits on mouse read */
  158.     MOUEVENTINFO event;     /* return for mouse reads */
  159.     PTRLOC loc;         /* data for MouSetPtrPos */
  160.  
  161.  
  162.     /* try to open and initialize mouse.  If none installed, Mouse will
  163.      *        stay 0 to show keyboard emulation must be used */
  164.     if (!MouOpen (0L, &Mouse))        /* get handle */
  165.         /* mouse is here and well, so initialize it */
  166.         MouSetDevStatus (&status, Mouse);
  167.  
  168.     /* get current screen mode and save it for restoring on exit */
  169.     if (VioGetMode (&Savemode, 0)) {
  170.         printf ("Error setting screen mode, exitting\n");
  171.         DosExit (EXIT_PROCESS, 0);          /* exit if error */
  172.     }
  173.     /* set screen mode to 640x200 b/w (Cga high resolution */
  174.     if (VioSetMode (&Highres, 0)) {
  175.         printf ("Error setting screen mode, exitting\n");
  176.         DosExit (EXIT_PROCESS, 0);          /* exit if error */
  177.     }
  178.  
  179.     /* set mouse pointer to middle of screen */
  180.     if (Mouse) {
  181.     loc.row=ptrrow;
  182.     loc.col=ptrcol;
  183.     MouSetPtrPos (&loc, Mouse);
  184.     }
  185.  
  186.     /* get screen buffer segment */
  187.     get_phys.pBuf=(PBYTE)0xb8000L;
  188.     get_phys.cb=16*1024L;
  189.     if (VioGetPhysBuf (&get_phys, 0)) {
  190.         /* if error here, restore screen and exit */
  191.         VioSetMode (&Savemode, 0);
  192.         printf ("Error accessing screen memory, exitting\n");
  193.         DosExit (EXIT_PROCESS, 0);
  194.     }
  195.     ScrSeg = get_phys.asel[0];           /* store in global */
  196.  
  197.     /* set ctrl-C to quit() to reset screen at even a break */
  198.     DosSetSigHandler (exitlife, (PFNSIGHANDLER FAR *)&throwaway,
  199.               &throwaway, 2, 1);
  200.  
  201.     /* allocate a segment to hold internal grid representation */
  202.     if (DosAllocSeg (InRow*InCol/BYTEINCOLS, (PSEL) &InGrid, 0))
  203.         /* from here on use anerror() to report errors */
  204.         anerror ("Error allocating memory", 1);
  205.     (long) InGrid *= 0x10000L;            /* move selector to high word*/
  206.  
  207.     /* allocate a segment to hold second buffer used in stepping */
  208.     if (DosAllocSeg(InRow*InCol/BYTEINCOLS, (PSEL) &InGrid2, 0))
  209.         anerror ("Error allocating memory", 1);
  210.     (long) InGrid2 *= 0x10000L;         /* move selector to high word*/
  211.  
  212.     /* blank internal grid and draw screen */
  213.     for (i=InRow*InCol/16, ptr=(unsigned far *) InGrid ; i--;)
  214.         *ptr++ = 0;
  215.     showingrid ();
  216.     xorptr (ptrrow, ptrcol);            /* show cursor */
  217.  
  218.  
  219.     /* main input loop, polling mouse and keyboard */
  220.     while (1) {
  221.     /* try to get mouse event from keyboard or mouse */
  222.     kbd.chChar=0;                 /* clear key buffer residue */
  223.     kbdreadeventque (&event, ptrrow, ptrcol);
  224.     if (Mouse)
  225.         MouReadEventQue (&event, &type, Mouse);
  226.  
  227.     /* if any mouse-like events, do this */
  228.     if (event.fs) {
  229.         xorptr (ptrrow, ptrcol);        /* hide cursor */
  230.         if (event.fs & 1+2+8) {      /* if any motion */
  231.             /* update position, if off screen grid,ptr=max or min*/
  232.             ptrrow = event.row;
  233.             ptrcol = event.col;
  234.             if (ptrrow > MouBoundRow)
  235.             ptrrow = MouBoundRow;
  236.             if (ptrcol > MouBoundCol)
  237.             ptrcol = MouBoundCol;
  238.         }
  239.         /* if a button was hit */
  240.         if (event.fs & (2|4|8|16)) {
  241.         if (ptrrow < COM_ROW*FONT_ROW-SizeScrRow) { /* if on grid */
  242.             Generation=0;               /* reset Gen if     */
  243.             putgen();                   /*   grid modified*/
  244.             if (event.fs & (2 | 4))         /* if left down*/
  245.             fill (ptrcol/SizeScrCol, ptrrow/SizeScrRow);
  246.             else if (event.fs & (8 | 16))    /* if right */
  247.             remove (ptrcol/SizeScrCol, ptrrow/SizeScrRow);
  248.         }
  249.         else {                       /* on command line*/
  250.             /* if pointing at command word, execute it, highlighting */
  251.             for(i=0; ComLine[i].Fun != 0; i++)
  252.             if (ptrcol/BIT >= ComLine[i].Col &&
  253.                 ptrcol/BIT < ComLine[i+1].Col-1) {
  254.                 gputs(ComLine[i].Name,COM_ROW,ComLine[i].Col,0xff);
  255.                 wait4release ();        /* execute on release*/
  256.                 (*(ComLine[i].Fun))();
  257.                 if (ComLine[i].Fun) /*restore name if not removed*/
  258.                    gputs(ComLine[i].Name,COM_ROW,ComLine[i].Col,0);
  259.             }
  260.         }
  261.         }
  262.         xorptr (ptrrow, ptrcol);           /* show cursor */
  263.     }
  264.  
  265.     /* check keys, no wait */
  266.     KbdPeek (&kbd, 0);
  267.     if (kbd.chChar) {         /* get key only if not regular char */
  268.         KbdCharIn (&kbd, 1, 0);  /*get command*/
  269.         xorptr (ptrrow, ptrcol);        /* avoid overwrites */
  270.         /* walk command structure and execute function, highlighting */
  271.         for(i=0; ComLine[i].Fun != 0; i++)
  272.             if ((kbd.chChar & 0xdf) == *(ComLine[i].Name)) {
  273.             gputs(ComLine[i].Name,COM_ROW,ComLine[i].Col,0xff);
  274.             (*(ComLine[i].Fun))();
  275.             if (ComLine[i].Fun) /*restore name if not removed*/
  276.                 gputs(ComLine[i].Name,COM_ROW,ComLine[i].Col,0);
  277.             }
  278.         xorptr (ptrrow, ptrcol);        /* restore pointer */
  279.     }
  280.     }
  281. }
  282.  
  283.  
  284. /***    blank - blank internal and screen grid
  285.  *
  286.  *    Sets the internal grid to all 0's and calls draw_grid() to
  287.  *    clear the screen and put up a screen grid.  blank() then draws
  288.  *    the command line and the program logo.
  289.  *
  290.  *    Entry:    ScrSeg = current screen segment
  291.  *        InGrid points to internal grid defined by InCol and InRow
  292.  *        Logo points to program logo that also clears the prompt line
  293.  *
  294.  *    Exit:    Generation = 0
  295.  *
  296.  *    Calls:    gputs(), putgen(), draw_grid [assembler routine]
  297.  */
  298. void
  299. blank (){
  300.     unsigned far *ptr;
  301.     int i;
  302.  
  303.     gputs (Logo, PROMPT_ROW, PROMPT_COL, 0);
  304.     gputs ("Are you sure?", PROMPT_ROW, PROMPT_COL, 0);
  305.     if (gethit () == 'Y') {
  306.         /* if yes, blank screen and internal grid */
  307.         draw_grid ();            /* draw blank grid on screen */
  308.         for (i=InRow*InCol/16, ptr=(unsigned far *) InGrid ; i--;)
  309.             *ptr++ = 0;        /* blank internal grid */
  310.         for (i=0; ComLine[i].Fun != 0; i++) /* print command line */
  311.             gputs (ComLine[i].Name, COM_ROW, ComLine[i].Col, 0);
  312.         Generation=0;            /* reset generation count */
  313.     }
  314.     gputs (Logo, PROMPT_ROW, PROMPT_COL, 0);   /* put on logo */
  315.     putgen ();                /* initial generation message*/
  316. }
  317.  
  318.  
  319. /***    go - steps through generations until a key is pressed
  320.  *
  321.  *    Calls step() until a key is hit, which terminates the loop
  322.  *    and the key is left on the buffer for main() to process
  323.  *    as a command.
  324.  *    step()'s return is checked to see if this program has just
  325.  *    moved from background to foreground, in which case the
  326.  *    the screen will need manual updating with showingrid().
  327.  *    If the D or U keys are hit, down() or up() is executed.
  328.  *
  329.  *    Entry:    none
  330.  *
  331.  *    Exit:    None
  332.  *
  333.  *    Calls:    step(), showingrid()
  334.  */
  335. void
  336. go () {
  337.     KBDKEYINFO kbd;                /* return from KBD call */
  338.     int background=0;                /* 0 if forground, 1 backgrd */
  339.     MOUEVENTINFO event;             /* return from mouse read */
  340.     USHORT type=0;                /* for no waits on mouse read*/
  341.     MOUQUEINFO num;                /* return for GetNumQueEl */
  342.     unsigned x,y;                /* for slowing down loops */
  343.  
  344.     /* step until key or button is hit, executing GoAble commands */
  345.     kbd.chScan=0;
  346.     event.fs=0;
  347.     while (!kbd.chScan) {
  348.     if (step ())
  349.         background=1;
  350.     else if (background==1) {
  351.         background=0;
  352.         showingrid();
  353.     }
  354.     if (Mouse) {
  355.         /* read all events on que */
  356.         MouGetNumQueEl (&num, Mouse);
  357.         while (num.cEvents--) {
  358.             MouReadEventQue (&event, &type, Mouse);
  359.             if (event.fs & (2|4|8|16)) {  /* leave if mouse hit*/
  360.                 wait4release();
  361.                 return;
  362.             }
  363.         }
  364.     }
  365.  
  366.     /* check keys, no wait.  Execute if GoAble command */
  367.     KbdPeek (&kbd, 0);
  368.     if (kbd.chScan) {
  369.         /* walk command structure and execute function, highlighting */
  370.         for(x=0; ComLine[x].Fun != 0; x++)
  371.             if ((kbd.chChar & 0xdf) == *(ComLine[x].Name)
  372.                    && ComLine[x].GoAble) {
  373.             gputs(ComLine[x].Name,COM_ROW,ComLine[x].Col,0xff);
  374.             KbdCharIn (&kbd, 1, 0);
  375.             kbd.chScan=0;
  376.             (*(ComLine[x].Fun))();
  377.             if (ComLine[x].Fun) /*restore name if not removed*/
  378.                 gputs(ComLine[x].Name,COM_ROW,ComLine[x].Col,0);
  379.             }
  380.     }
  381.     if (Slow)           /* slow down if requested */
  382.         for (x=Slow; x--;)
  383.             for (y=50000; y--;);
  384.     }
  385. }
  386.  
  387.  
  388. /***    halt - does nothing
  389.  */
  390. void
  391. halt () {
  392.     int i;
  393.  
  394.     for (i=30000; i--;);        /* short pause */
  395. }
  396.  
  397.  
  398. /***    quit - exit after prompting for certainty
  399.  *
  400.  *    Entry:    None
  401.  *
  402.  *    Exit:    None
  403.  */
  404. void
  405. quit () {
  406.     gputs (Logo, PROMPT_ROW, PROMPT_COL, 0);
  407.     gputs ("Are you sure?", PROMPT_ROW, PROMPT_COL, 0);
  408.     if (gethit () == 'Y')
  409.         exitlife ();            /* if yes, do quit routine */
  410.     gputs (Logo, PROMPT_ROW, PROMPT_COL, 0);   /* else, continue */
  411.     putgen();                /* put back gen count*/
  412. }
  413.  
  414.  
  415. /***    diskread - read new grid from disk
  416.  *
  417.  *    getfilespec() is called to get a file name from the user which
  418.  *    is put in Filespec.
  419.  *    Then an internal grid is read from the disk which is in the form:
  420.  *                WORD SIGNATURE ; Life file signature
  421.  *                WORD rows
  422.  *                WORD columns
  423.  *                WORD generation
  424.  *    (rows*columns/BYTEINCOLS) BYTES of the bit mapped grid
  425.  *
  426.  *    InGrid is ReAlloced to the size of the newly read grid and
  427.  *    InCol, InRow, and Generation are all set the values contained
  428.  *    in the file read.
  429.  *
  430.  *    Entry:    InGrid points to internal grid defined by InCol and InRow
  431.  *
  432.  *    Exit:    InGrid is ReAlloced to size of newly read grid.
  433.  *        InCol, InRow, and Generation are all set the values
  434.  *            contained in the file read.
  435.  *                         d
  436.  *    Calls:    getfilespec ();
  437.  */
  438. void
  439. diskread () {
  440.     HFILE handle;                /* file handle */
  441.     USHORT action;                /* return for file calls */
  442.     unsigned signature;            /* life file signature word */
  443.  
  444.     /* get file name into Filespec */
  445.     if (getfilespec ())
  446.         return;             /* return if user hit ESC */
  447.  
  448.     /* open file, fail if it doesn't exist */
  449.     if (DosOpen ((PSZ) Filespec, &handle,
  450.               &action, 0L, 0, 0x01, 0x42, 0L)) {
  451.         anerror ("Can't open file", 0);
  452.         Filespec[0]=0;    /* clear bad file name */
  453.         return;
  454.     }
  455.  
  456.     /* read appropriate from file */
  457.     if (DosRead (handle, (PSZ) &signature, sizeof (signature),
  458.              &action) || action != sizeof (signature))
  459.         anerror ("Error writing to file", 0);
  460.  
  461.     else if (signature != SIGNATURE) {
  462.         anerror ("Not a life file", 0);
  463.         if (DosClose (handle))
  464.             anerror ("Error closing file", 0);
  465.         return;
  466.     }
  467.     else if (DosRead (handle, (PSZ) &InRow, sizeof (InRow),
  468.               &action) || action != sizeof (InRow))
  469.         anerror ("Error reading from file", 0);
  470.  
  471.     else if (DosRead (handle, (PSZ) &InCol, sizeof (InCol),
  472.               &action) || action != sizeof (InCol))
  473.         anerror ("Error reading from file", 0);
  474.  
  475.     else if (DosRead (handle, (PSZ) &Generation,
  476.         sizeof (Generation), &action) ||
  477.         action != sizeof (Generation))
  478.         anerror ("Error reading from file", 0);
  479.  
  480.     /* change size of *InGrid to match saved pattern */
  481.     else if (DosReallocSeg (InRow*InCol/BYTEINCOLS, (unsigned)
  482.         ((long) InGrid/0x10000L)))
  483.         anerror ("Error allocating memory", 0);
  484.  
  485.     else if (DosRead (handle, InGrid, InRow*InCol/BYTEINCOLS,
  486.               &action) || action != InRow*InCol/BYTEINCOLS)
  487.         anerror ("Error reading from file", 0);
  488.  
  489.     /* show the newly loaded pattern on the screen */
  490.     showingrid ();
  491.  
  492.     /* close file */
  493.     if (DosClose (handle))
  494.         anerror ("Error closing file", 0);
  495. }
  496.  
  497.  
  498. /***    step - advance one generation on screen and internally
  499.  *
  500.  *    Uses dostep to advance the current internal and screen grid
  501.  *    to the next generation.  dostep() returns 1 if the screen
  502.  *    was not available and thus no update was made, this allows
  503.  *    the program to continue to execute in the background.
  504.  *    The return from dostep() is passed back to step()'s caller.
  505.  *
  506.  *    Entry:    None
  507.  *
  508.  *    Exit:    Returns 1 if executing in background and there was thus
  509.  *              no screen update.
  510.  *        Else, returns 0 if in forground
  511.  *        Generation is incremented.
  512.  *
  513.  *    Calls:    putgen(), dostep [assembler routine]
  514.  */
  515. int
  516. step() {
  517.     int    rc;                /* return code from dostep */
  518.  
  519.     /* do the stepping using an assembler routine for speed */
  520.     rc=dostep(InGrid, InGrid2, InRow, InCol);  /* do the step */
  521.  
  522.     Generation++;                /* advance the count */
  523.     putgen();                /* and display gen */
  524.     return(rc);
  525. }
  526.  
  527.  
  528. /***    diskwrite - write internal grid to disk
  529.  *
  530.  *    getfilespec() is called to get a file name from the user which
  531.  *    is put in Filespec.
  532.  *    Then the internal grid is saved to disk in the form:
  533.  *                WORD SIGNATURE ; Life file signature
  534.  *                WORD rows
  535.  *                WORD columns
  536.  *                WORD generation
  537.  *    (rows*columns/BYTEINCOLS) BYTES of the bit mapped grid
  538.  *
  539.  *    Entry:    InGrid points to internal grid defined by InCol and InRow
  540.  *
  541.  *    Exit:    None
  542.  *
  543.  *    Calls:    getfilespec ();
  544.  */
  545. void
  546. diskwrite () {
  547.     HFILE handle;                /* file handle */
  548.     USHORT action;                /* return for file calls */
  549.     unsigned signature=SIGNATURE;        /* life file signature word */
  550.  
  551.     /* get file name into Filespec */
  552.     if (getfilespec ())
  553.         return;             /* return if user hit ESC */
  554.  
  555.     /* open file and truncate or create it if it doesn't exist */
  556.     if (DosOpen ((PSZ) Filespec, &handle,
  557.               &action, 0L, 0, 0x12, 0x42, 0L)) {
  558.         anerror ("Can't open file", 0);
  559.         Filespec[0]=0;    /* clear bad file name */
  560.         return;
  561.     }
  562.  
  563.     /* write appropriate info to file */
  564.     if (DosWrite (handle, (PSZ) &signature, sizeof (signature),
  565.               &action)     || action != sizeof (signature))
  566.         anerror ("Error writing to file", 0);
  567.  
  568.     else if (DosWrite (handle, (PSZ) &InRow, sizeof (InRow),
  569.                &action) || action != sizeof (InRow))
  570.         anerror ("Error writing to file", 0);
  571.  
  572.     else if (DosWrite (handle, (PSZ) &InCol, sizeof (InCol),
  573.                &action) || action != sizeof (InCol))
  574.         anerror ("Error writing to file", 0);
  575.  
  576.     else if (DosWrite (handle, (PSZ) &Generation,
  577.         sizeof (Generation), &action) ||
  578.         action != sizeof (Generation))
  579.         anerror ("Error writing to file", 0);
  580.  
  581.     else if (DosWrite (handle, InGrid, InRow*InCol/BYTEINCOLS,
  582.                &action) || action != InRow*InCol/BYTEINCOLS)
  583.         anerror ("Error writing to file", 0);
  584.  
  585.     /* close file */
  586.     if (DosClose (handle))
  587.         anerror ("Error closing file", 0);
  588. }
  589.  
  590.  
  591. /***    up - speeds up GOs (decrement Slow)
  592.  */
  593. void
  594. up() {
  595.     int i;
  596.  
  597.     if (Slow)
  598.         Slow--;
  599.     if (!Slow) {
  600.         /* if at top speed take out message */
  601.         for(i=0; *(ComLine[i].Name) != 'U'; i++);
  602.         ComLine[i].Fun=0;
  603.         gputs ("        ", COM_ROW, ComLine[i].Col, 0);
  604.     }
  605.     for (i=30000; i--;);        /* short pause */
  606. }
  607.  
  608.  
  609. /***    down - slows down GOs (increment Slow)
  610.  */
  611. void
  612. down() {
  613.     int i;
  614.  
  615.     Slow++;
  616.     for (i=30000; i--;);        /* short pause */
  617.     if (Slow){            /* if at top speed, display UP */
  618.         for(i=0; *(ComLine[i].Name) != 'U'; i++);
  619.         ComLine[i].Fun=up;
  620.         for (i=0; ComLine[i].Fun != 0; i++) /* print command line */
  621.             gputs (ComLine[i].Name, COM_ROW, ComLine[i].Col, 0);
  622.     }
  623. }
  624.  
  625.  
  626. /***    beep - beep the speaker for bad commands
  627.  */
  628. void
  629. beep () {
  630.     DosBeep (500, 50);
  631. }
  632.  
  633.  
  634. /***    showingrid - display internal grid on screen
  635.  *
  636.  *    Clears the screen using the draw_grid routine and then
  637.  *    puts the internal grid on the screen using fill() so as to
  638.  *    not be resolution dependent.
  639.  *
  640.  *    Entry:    InGrid points to internal grid defined by InCol and InRow.
  641.  *        ScrSeg points to the screen buffer.
  642.  *        ScrRow and ScrCol describe the dimensions of the screen grid.
  643.  *        Logo points to program logo that also clears the prompt line
  644.  *
  645.  *    Exit:    None
  646.  *
  647.  *    Calls:    fill(), putgen(), gputs(), draw_grid [assembler routine]
  648.  */
  649. void
  650. showingrid () {
  651.     int x, y;                /* cell coordinates for loop */
  652.     BYTE retcode;                  /* for VIOSCRLOCK */
  653.  
  654.     /* prepare blank screen so we only have to fill in cells that are on */
  655.     draw_grid ();                /* draw blank grid on screen */
  656.     /* print command line */
  657.     for (x=0; ComLine[x].Fun != 0; x++)
  658.         gputs (ComLine[x].Name, COM_ROW, ComLine[x].Col, 0);
  659.     gputs (Logo, PROMPT_ROW, PROMPT_COL, 0);
  660.     putgen ();
  661.  
  662.     /* loop through the screen sized area of the grid */
  663.     VioScrLock (1, &retcode, 0);   /* get screen access */
  664.     for (y=ScrRow; y--;)
  665.         for (x=ScrCol; x--;)
  666.             /* if the internal cell is on, turn on the screen one*/
  667.             if (*(InGrid + (x + y*InCol)/BYTEINCOLS) & (0x80 >> (x&7)))
  668.                 fill (x, y);
  669.     VioScrUnLock (0);
  670. }
  671.  
  672.  
  673. /***    getfilespec - get a file name from the user
  674.  *
  675.  *    Prompts user for a file name for use with diskread() or diskwrite().
  676.  *    The last file name used is shown on the screen as a default
  677.  *    and the user can hit enter (or left mouse button) to specify the
  678.  *    default name.  If any other key is pressed, this routine takes the
  679.  *    input until an enter and returns in Filespec.  If ESC is hit any time
  680.  *    during typeing, the input will be aborted and the buffer cleared.
  681.  *
  682.  *    Entry:    Filespec points to default file name
  683.  *        Logo points to program logo that also clears the prompt line
  684.  *
  685.  *    Exit:    Filespec points to new file name
  686.  *        returns 0x1b if ESC was hit during entry, else 0
  687.  *
  688.  *    Calls:    gputs(), putgen()
  689.  */
  690. int
  691. getfilespec () {
  692.     int c=0;                /* index for Filespec[] */
  693.     KBDKEYINFO kbd;             /* return for KBD call */
  694.     unsigned space=0x0020;            /* space character for print */
  695.     MOUEVENTINFO event;            /* return for mouse */
  696.     USHORT type=0;                /* mouse reads w/no wait */
  697.  
  698.     kbd.chScan=0;
  699.     event.fs=0;
  700.     /* put up default file name if one exists */
  701.     if (Filespec[0]) {
  702.         gputs ("Type file name or enter for default:  ", PROMPT_ROW,
  703.                                  PROMPT_COL,0);
  704.         gputs (Filespec, PROMPT_ROW, FILE_COL, 0);
  705.         /* wait for first key or button to see if they accept default */
  706.         while (!kbd.chScan && !(event.fs & (2|4|8|16))) {
  707.             if (Mouse)
  708.                 MouReadEventQue (&event, &type, Mouse);
  709.             KbdPeek (&kbd, 0);
  710.         }
  711.     }
  712.     if (event.fs & (8 | 16)) {    /* if right button hit, same as ESC */
  713.         kbd.chChar = 0x1b;
  714.         wait4release();
  715.     }
  716.     if (event.fs & (2 | 4)) {     /* if left button hit, same as \r */
  717.         wait4release();
  718.         gputs (Logo, PROMPT_ROW, PROMPT_COL, 0);
  719.         putgen();
  720.         return (0);
  721.     }
  722.     if (kbd.chChar == 0x1b) {    /* if ESC, return */
  723.         gputs (Logo, PROMPT_ROW, PROMPT_COL, 0);
  724.         putgen();
  725.         return (0x1b);               /*    return escape */
  726.     }
  727.  
  728.     gputs(Logo, PROMPT_ROW, PROMPT_COL, 0);
  729.     gputs("Type file name,  followed by enter:  ",PROMPT_ROW,PROMPT_COL,0);
  730.  
  731.     /* read chars and print them until \r */
  732.     while (!(KbdCharIn (&kbd, 0, 0)) &&
  733.        kbd.chChar != '\r') {
  734.          if (kbd.chChar == '\b') { /* if backspace, backspace */
  735.              if (c > 0) {            /* prevent underflow*/
  736.                  Filespec[--c]=0;
  737.                  /* erase backed-out character*/
  738.                  gputs (&space, PROMPT_ROW, FILE_COL+c,0);
  739.              }
  740.              else                /* if overflow, yell */
  741.                  beep();
  742.          }
  743.          else if (kbd.chChar == 0x1b) {         /* if escape was hit */
  744.              Filespec[0]=0;            /*   blank name */
  745.              gputs (Logo, PROMPT_ROW, PROMPT_COL, 0);
  746.              putgen();
  747.              return (0x1b);            /*   return escape */
  748.          }
  749.          else if (kbd.chChar <46 || kbd.chChar > 122)
  750.              beep();                /* if not char, beep */
  751.          else
  752.              if (c < sizeof (Filespec)) {    /* prevent overflow */
  753.                  Filespec[c]=kbd.chChar;  /* add to string */
  754.                  Filespec[++c]=0;         /* null terminal */
  755.              }
  756.              else                /* if overflow, yell */
  757.                  beep();
  758.  
  759.          gputs (Filespec, PROMPT_ROW, FILE_COL, 0);   /* print new name */
  760.     }
  761.     /* return generation count to screen */
  762.     gputs (Logo, PROMPT_ROW, PROMPT_COL, 0);
  763.     putgen();
  764.     return (0);
  765. }
  766.  
  767.  
  768. /***    putgen - puts the current generation counter on prompt line
  769.  *
  770.  *    Entry:    Generation = current generation number
  771.  *
  772.  *    Exit:    None
  773.  *
  774.  *    Calls:    gputs()
  775.  */
  776. void
  777. putgen ()
  778. {
  779.     char num[8];                /* number string to print */
  780.     int i, g;
  781.  
  782.     gputs ("Generation:         ", PROMPT_ROW, PROMPT_COL,0);/* gen label*/
  783.  
  784.     if (Generation == 0)
  785.         gputs ("0", PROMPT_ROW,GEN_COL,0);/*if special case need this*/
  786.     else {                    /* not 0, do this: */
  787.         for (g=Generation, i=sizeof(num)-1; i-- >= 0 && g; g/=10)
  788.             num[i]=(g%10)+'0';      /* convert int to string */
  789.         num[sizeof(num)-1]= 0;         /* zero terminated for gputs */
  790.         gputs (&num[i+1], PROMPT_ROW, GEN_COL,0); /* print number */
  791.     }
  792. }
  793.  
  794.  
  795. /***    fill -    fill in a grid cell on the screen and internally
  796.  *
  797.  *    fill (x, y)
  798.  *
  799.  *    Entry:    x = grid horizontal coordinate
  800.  *        y = grid vertical coordinate
  801.  *        ScrSeg = screen buffer segment
  802.  *        VioScrLock is active
  803.  *        InGrid points to internal grid defined by InCol and InRow
  804.  *        ScrRow and ScrCol define limits of screen grid
  805.  *        Currently, must use 79x25 grid drawn by draw_grid()
  806.  *        Currently, screen must be in 640x200 b/w mode
  807.  *
  808.  *    Exit:    None
  809.  */
  810. void
  811. fill (x, y)
  812. int x,y;
  813. {
  814.     char far *gridptr;
  815.  
  816.     /* only do if within screen grid limits */
  817.     if (x < ScrCol && y < ScrRow) {
  818.         /* set gridptr to & in screen buff of cell to fill */
  819.         (long) gridptr = ScrSeg*0x10000;      /* fill in address */
  820.         (unsigned) gridptr = (x*SizeScrCol/BIT)+(y*SizeScrRow/Cga*SCR_WID);
  821.         /* fill in screen cell */
  822.         *(gridptr+OddPage)=0x7f;
  823.         *(gridptr+SCR_WID)=0x7f;
  824.         *(gridptr+OddPage+SCR_WID)=0x7f;
  825.     }
  826.     /* fill in internal cell*/
  827.     /*InGrid= &internal grid of x,y*/
  828.     gridptr = InGrid + x/BYTEINCOLS + y*InCol/BYTEINCOLS;
  829.     *gridptr = *gridptr | (0x80 >> (x & 7));   /* fill bit */
  830. }
  831.  
  832.  
  833. /***    remove - clear a grid cell on the 79x45 grid screen and internally
  834.  *
  835.  *    remove (x, y)
  836.  *
  837.  *    Entry:    x = grid horizontal coordinate
  838.  *        y = grid vertical coordinate
  839.  *        ScrSeg = screen buffer segment
  840.  *        VioScrLock is active
  841.  *        InGrid points to internal grid defined by InCol and InRow
  842.  *        ScrRow and ScrCol define limits of screen grid
  843.  *        Currently, must use 79x25 grid drawn by draw_grid()
  844.  *        Currently, screen must be in 640x200 b/w mode
  845.  *
  846.  *    Exit:    None
  847.  */
  848. void
  849. remove (x, y)
  850. int x,y;
  851. {
  852.     char far *gridptr;
  853.  
  854.     /* only do if within screen grid limits */
  855.     if (x < ScrCol && y < ScrRow) {
  856.         /* set gridptr to & in screen buff of cell to fill */
  857.         (long) gridptr = ScrSeg*0x10000;      /* fill in address */
  858.         (unsigned) gridptr = (x*SizeScrCol/BIT)+(y*SizeScrRow/Cga*SCR_WID);
  859.         /* fill in screen cell */
  860.         *(gridptr+OddPage)=0x80;
  861.         *(gridptr+SCR_WID)=0x80;
  862.         *(gridptr+OddPage+SCR_WID)=0x80;
  863.     }
  864.  
  865.     /* remove internal cell*/
  866.     /*InGrid= &internal grid of x,y*/
  867.     gridptr = InGrid + x/BYTEINCOLS + y*InCol/BYTEINCOLS;
  868.     *gridptr = *gridptr & ~((0x80 >> (x & 7)));   /* remove bit */
  869. }
  870.  
  871.  
  872. /***    kbdreadeventque - simulates MouReadEventQue with the keyboard
  873.  *
  874.  *    F9 key is responded to as left button, F10 key as right.
  875.  *    Pointer location is advanced by SizeScrCol or SizeScrRow according
  876.  *    to direction of direction key hits.
  877.  *    Does not wait for input, instead returns 0 event fs.
  878.  *    Does not check MouDevStatus, instead always returns pixel coordinates
  879.  *    Event buffer is filled according to MouReadEventQue
  880.  *
  881.  *    Entry:    event  - structure to return data
  882.  *        ptrrow - current point row
  883.  *        ptrcol - current pointer column
  884.  *
  885.  *    Exit:    None
  886.  *        *event contains any event that may have occurred
  887.  */
  888. int
  889. kbdreadeventque (event, ptrrow, ptrcol)
  890. MOUEVENTINFO *event;
  891. int ptrrow, ptrcol;
  892. {
  893.     KBDKEYINFO kbd;             /* return for KBD call */
  894.  
  895.     kbd.chChar=0;                 /* clear data struct residue*/
  896.     kbd.chScan=0;
  897.     /* peek at key buffer */
  898.     KbdPeek (&kbd, 0);
  899.  
  900.     /* if it is an extended key code, process it */
  901.     if (kbd.chChar == 0 && kbd.chScan) {
  902.         KbdCharIn (&kbd, 1, 0); /* take char */
  903.         event->row=ptrrow;        /* initial position */
  904.         event->col=ptrcol;
  905.         switch (kbd.chScan) {         /* check second byte of code */
  906.             case 0x48:            /* cursor up */
  907.             event->row -= SizeScrRow;
  908.             event->fs=1;
  909.             break;
  910.             case 0x50:            /* cursor down */
  911.             event->row += SizeScrRow;
  912.             event->fs=1;
  913.             break;
  914.             case 0x4b:            /* cursor left */
  915.             event->col -= SizeScrCol;
  916.             event->fs=1;
  917.             break;
  918.             case 0x4d:            /* cursor right */
  919.             event->col += SizeScrCol;
  920.             event->fs=1;
  921.             break;
  922.             case 0x43:            /* left button (F9) */
  923.             event->fs=4;
  924.             break;
  925.             case 0x52:            /* left button (INS) */
  926.             event->fs=4;
  927.             break;
  928.             case 0x44:             /* right button (F10) */
  929.             event->fs=16;
  930.             break;
  931.             case 0x53:             /* right button (DEL) */
  932.             event->fs=16;
  933.             break;
  934.             default:            /* anything else is no good */
  935.             event->fs=0;
  936.             break;
  937.         }
  938.     }
  939.     else
  940.         event->fs=0;              /* if no extended key */
  941. }
  942.  
  943.  
  944. /***    xorptr - xors the mouse ptr on the screen
  945.  *
  946.  *    xorptr (ptrrow, ptrcol)
  947.  *
  948.  *    Entry:    ptrrow = pixel row on screen
  949.  *        ptrcol = pixel column on screen
  950.  *        ScrSeg = screen buffer segment
  951.  *
  952.  *    Exit:    None
  953.  */
  954. void
  955. xorptr (ptrrow, ptrcol)
  956. unsigned ptrrow, ptrcol;
  957. {
  958.     char far *gridptr;
  959.     unsigned mask=0;              /* bit mask for pointer shape*/
  960.     int x=8;                  /* count for rows of pointer */
  961.     BYTE retcode;                /* return from VioScrLock */
  962.  
  963.     /* set gridptr to & in screen buff to xor */
  964.     ptrrow++;                /* 1st move ptr to next row*/
  965.     (long) gridptr = ScrSeg*0x10000;    /* fill in segment address */
  966.     (unsigned) gridptr = ptrcol/x+(ptrrow/Cga*SCR_WID);/* fill offset*/
  967.  
  968.     /* fill in screen pointer */
  969.     VioScrLock (1, &retcode, 0);       /* get screen */
  970.     while (x--) {
  971.         mask = mask >> 1;            /* make mask thicker */
  972.         mask |= 0x8000;             /*    at bottom */
  973.         if (ptrrow & 1 && Cga == 2)        /* if odd bit plane */
  974.             gridptr += OddPage;
  975.         /* xor mask to scr, being sure to reverse bytes in word */
  976.         *(gridptr+1) ^= (char) (mask >> (ptrcol & 7));
  977.         *(gridptr) ^= (char) ((mask >> (ptrcol & 7)) / 256);
  978.         if (ptrrow & 1 && Cga == 2)        /* if odd bit plane */
  979.             gridptr -= OddPage;        /*    plane, next row*/
  980.         ptrrow++;                /* next row */
  981.         if (!(ptrrow & 1) && Cga == 2)        /*only advance on odd*/
  982.             gridptr += SCR_WID;        /*   row w/o CGA */
  983.      }
  984.     VioScrUnLock (0);                /* give back screen */
  985. }
  986.  
  987.  
  988. /***    gputs - put a character string on the graphics screen
  989.  *
  990.  *    gputs (string, row, col)
  991.  *
  992.  *    Draws the passed asciiz string on the screen unless the screen
  993.  *    is not available (i.e., if running in background screen group)
  994.  *    in which case this routine does nothing so as to not hold up
  995.  *    the program.
  996.  *
  997.  *    Entry:    string - address of asciiz string to print
  998.  *        row    - alpha row to print at
  999.  *        col    - alpha col to print at
  1000.  *        mask   - xor'ed with bytes of character as printed
  1001.  *
  1002.  *    Exit:    None
  1003.  *
  1004.  *    Calls:    gputchar()
  1005.  *
  1006.  *    Warning: Prints graphics characters for ascii control codes (<32).
  1007.  *         Does not wrap-around at line end.
  1008.  */
  1009. void
  1010. gputs (string, row, col, mask)
  1011. char *string;
  1012. unsigned col, row;
  1013. char mask;
  1014. {
  1015.     static VIOFONTINFO fontdata = {14,1,FONT_ROW,8,0L,0};/*for GetFont*/
  1016.     char c;
  1017.     BYTE retcode;                  /* for VioScrLock */
  1018.     char far *scr_addr;            /* address in screen buffer
  1019.                            to print at */
  1020.  
  1021.     /* try lock without waiting for availability */
  1022.     VioScrLock (0, &retcode, 0);       /* get screen access */
  1023.  
  1024.     /* do display only if screen available now */
  1025.     if (retcode == 0) {
  1026.         /* get address of font table if haven't yet */
  1027.            if (!fontdata.pbData)
  1028.                VioGetFont (&fontdata, 0);
  1029.  
  1030.            (long) scr_addr = ScrSeg*0x10000;   /* fill in segment address*/
  1031.            (unsigned)scr_addr = col+(row*SCR_WID*(FONT_ROW/Cga));/*offset*/
  1032.            while ((c=*(string++)) != 0)           /* print each char */
  1033.               gputchar(scr_addr++,
  1034.                 (ULONG)fontdata.pbData+c*FONT_ROW,mask);
  1035.            VioScrUnLock (0);               /* give up screen */
  1036.     }
  1037. }
  1038.  
  1039.  
  1040. /***    gputchar - put a character on the graphics screen
  1041.  *
  1042.  *    gputchar (scr_addr, font_addr, mask)
  1043.  *
  1044.  *    Entry:    scr_addr  - far address in screen buffer to place character
  1045.  *                must be on even Cga bit plane
  1046.  *        font_addr - far address in font table bit pattern
  1047.  *        mask      - xor'ed with each byte put on screen
  1048.  *        VioScrLock is active
  1049.  *
  1050.  *    Exit:    None
  1051.  */
  1052. void
  1053. gputchar(scr_addr, font_addr, mask)
  1054. char far *scr_addr;
  1055. char far *font_addr;
  1056. char mask;
  1057. {
  1058.     int i=FONT_ROW;         /* count to draw whole character */
  1059.  
  1060.     while (i--) {
  1061.         if (i & 1 && Cga == 2)        /* if odd bit plane */
  1062.             scr_addr += OddPage-SCR_WID;
  1063.         *scr_addr = (*(font_addr++) ^ mask);/* put byte on even plane*/
  1064.         if (i & 1 && Cga == 2)        /* if odd bit plane */
  1065.             scr_addr -= OddPage;
  1066.         scr_addr+=SCR_WID;        /* move down a line */
  1067.     }
  1068. }
  1069.  
  1070.  
  1071. /***    anerror - error handler
  1072.  *
  1073.  *    anerror (message, fatal);
  1074.  *
  1075.  *    Prints passed error message and waits for a key hit to allow
  1076.  *    user to read it.  If fatal flag is 0, the routine then returns,
  1077.  *    otherwise the routine restores the original screen mode, clears
  1078.  *    the screen, and exits.
  1079.  *
  1080.  *    Entry:    message - string to print describing error
  1081.  *        fatal - flag, if == 0, this procedure prompts for any key hit
  1082.  *            then returns.  If != 0, error is fatal & program exits.
  1083.  *        Logo points to program logo that also clears the prompt line
  1084.  *
  1085.  *    Exit:    None
  1086.  *
  1087.  *    Calls:    gputs(), putgen()
  1088.  */
  1089. void
  1090. anerror (message, fatal)
  1091. char message[];
  1092. int fatal;
  1093. {
  1094.     KBDKEYINFO kbd;             /* return for KBD call */
  1095.  
  1096.     gputs (Logo, PROMPT_ROW, PROMPT_COL,0);
  1097.     gputs (message, PROMPT_ROW, PROMPT_COL, 0);/* print passed message */
  1098.  
  1099.     if (fatal) {
  1100.         gputs(":  Press any key to exit",PROMPT_ROW,strlen(message),0);
  1101.         gethit ();
  1102.         exitlife();
  1103.     }
  1104.     else  {
  1105.         gputs (":  Press any key to continue",PROMPT_ROW,
  1106.                               strlen(message), 0);
  1107.         gethit ();
  1108.         gputs (Logo, PROMPT_ROW, PROMPT_COL, 0);
  1109.         putgen();
  1110.     }
  1111. }
  1112.  
  1113.  
  1114. /***    gethit - waits for a key hit or mouse button hit
  1115.  *
  1116.  *    Entry:    None
  1117.  *
  1118.  *    Exit:    Returns character hit (forced upper case) or 'Y' for left
  1119.  *        mouse button and 'N' for right mouse
  1120.  */
  1121. char
  1122. gethit () {
  1123.     KBDKEYINFO kbd;             /* for keyboard */
  1124.     MOUEVENTINFO event;            /* for mouse */
  1125.     USHORT type=0;
  1126.     char rc;                /* return code */
  1127.  
  1128.     /* loop till a hit */
  1129.     kbd.chScan=0;
  1130.     event.fs=0;
  1131.     while (!kbd.chScan && !(event.fs & (2|4|8|16))) {
  1132.         if (Mouse)
  1133.             MouReadEventQue (&event, &type, Mouse);
  1134.         KbdCharIn (&kbd, 1, 0);
  1135.     }
  1136.     if (kbd.chScan)              /* if key, return char */
  1137.         rc=kbd.chChar & 0xdf;
  1138.     else if (event.fs & (8 | 16)) {       /* if right */
  1139.         rc='N';
  1140.         wait4release ();
  1141.     }
  1142.     else if (event.fs & (2 | 4))  {       /* if left down*/
  1143.         rc='Y';
  1144.         wait4release ();
  1145.     }
  1146.     return (rc);
  1147. }
  1148.  
  1149.  
  1150. /***    wait4release - wait until mouse buttons are up before returning
  1151.  *
  1152.  *    Entry:    Mouse = mouse handle or 0 if no mouse
  1153.  */
  1154. void
  1155. wait4release () {
  1156.     MOUEVENTINFO event;
  1157.     USHORT type = 0;           /* if OS/2, wait for events */
  1158.  
  1159.     event.fs=2;               /* force 1st read */
  1160.     if (Mouse)            /* loop till no button events */
  1161.         while ((event.fs & (2|4|8|16)) || event.Time==0)
  1162.             MouReadEventQue (&event, &type, Mouse);
  1163.  
  1164. }
  1165.  
  1166.  
  1167. /***    exitlife - exit program, resetting original screen mode
  1168.  *
  1169.  *    Called by a Ctrl-C
  1170.  *
  1171.  *    Entry:    Savemode contains original screen mode info
  1172.  *
  1173.  *    Exit:    None
  1174.  */
  1175. void far pascal
  1176. exitlife() {
  1177.         VioSetMode (&Savemode, 0); /* restore*/
  1178.         VioScrollUp (0,0,-1,-1,-1, (char far *) Cell,0);   /* cls */
  1179.         DosExit (EXIT_PROCESS, 0);       /* exit all threads */
  1180. }
  1181. 
  1182.